package com.jinyang.jetpackdemo.activity.ui

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewGroup
import android.widget.Scroller

/**
 * @Author: LiuJinYang
 * @CreateDate: 2022/1/20
 * @Description:
 */
class HorizontalView : ViewGroup {
    private var lastX = 0
    private var lastY = 0

    /**
     * 当前子元素
     */
    private var currentIndex = 0
    private var childWidth = 0
    private var scroller: Scroller? = null

    /**
     * 增加速度检测,如果速度比较快的话,就算没有滑动超过一半的屏幕也可以
     */
    private var tracker: VelocityTracker? = null
    private var lastInterceptX = 0
    private var lastInterceptY = 0

    constructor(context: Context?) : super(context) {
        init()
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        init()
    }

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        init()
    }

    fun init() {
        scroller = Scroller(context)
        tracker = VelocityTracker.obtain()
    }

    /**
     * 重写onMeasure处理wrap_content属性的尺寸测量
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        //测量所有子元素(没有考虑它的padding和子元素的margin)
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        if (childCount == 0) {
            //如果没有子元素，就设置宽高都为0（简化处理,正常的话应该根据LayoutParams中的宽和高来做相应的处理）
            setMeasuredDimension(0, 0)
        } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            //宽和高都是AT_MOST，则设置宽度所有子元素的宽度的和；高度设置为第一个元素的高度；
            val childOne = getChildAt(0)
            val childWidth = childOne.measuredWidth
            val childHeight = childOne.measuredHeight
            setMeasuredDimension(childWidth * childCount, childHeight)
        } else if (widthMode == MeasureSpec.AT_MOST) {
            //如果宽度是wrap_content，则宽度为所有子元素的宽度的和
            val childOne = getChildAt(0)
            val childWidth = childOne.measuredWidth
            setMeasuredDimension(childWidth * childCount, heightSize)
        } else if (heightMode == MeasureSpec.AT_MOST) {
            //如果高度是wrap_content，则高度为第一个子元素的高度
            val childHeight = getChildAt(0).measuredHeight
            setMeasuredDimension(widthSize, childHeight)
        }
    }

    /**
     * 重写onLayout来布局子元素
     */
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val childCount = childCount
        //左边的距离
        var left = 0
        var child: View
        //遍历布局子元素
        for (i in 0 until childCount) {
            child = getChildAt(i)
            if (child.visibility != GONE) {
                //子元素不是GONE，则调用子元素的layout方法将其放置到合适的位置上
                val width = child.measuredWidth
                //赋值给子元素宽度变量
                childWidth = width
                //没有处理自身的padding以及子元素的margin,right是left+元素的宽度
                child.layout(left, 0, left + width, child.measuredHeight)
                //left是一直累加的
                left += width
            }
        }
    }

    /**
     * 重写onInterceptTouchEvent处理滑动冲突
     */
    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        var intercept = false
        val x = event.x.toInt()
        val y = event.y.toInt()
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                intercept = false
                //如果动画还没有执行完成,则打断
                if (!scroller!!.isFinished) {
                    scroller!!.abortAnimation()
                }
            }
            MotionEvent.ACTION_MOVE -> {
                val deltaX = x - lastInterceptX
                val deltaY = y - lastInterceptY
                //水平方向距离长  MOVE中返回true一次,后续的MOVE和UP都不会收到此请求
                if (Math.abs(deltaX) - Math.abs(deltaY) > 0) {
                    intercept = true //用户想水平滑动的，所以拦截
                    Log.i("wangshu", "intercept = true")
                } else {
                    intercept = false
                    Log.i("wangshu", "intercept = false")
                }
            }
            MotionEvent.ACTION_UP -> intercept = false
            else -> {}
        }
        //因为DOWN返回false,所以onTouchEvent中无法获取DOWN事件,这里要负责设置lastX,lastY
        lastX = x
        lastY = y
        lastInterceptX = x
        lastInterceptY = y
        return intercept
    }

    /**
     * 重写onTouchEvent方法使用Scroller来弹性滑动到其他页面
     */
    override fun onTouchEvent(event: MotionEvent): Boolean {
        tracker!!.addMovement(event)
        val x = event.x.toInt()
        val y = event.y.toInt()
        when (event.action) {
            MotionEvent.ACTION_DOWN ->//ACTION_DOWN处理再次触摸屏幕阻止页面继续滑动
                if (!scroller!!.isFinished) {
                    scroller!!.abortAnimation()
                }
            MotionEvent.ACTION_MOVE -> {
                //跟随手指滑动
                val deltaX = x - lastX
                scrollBy(-deltaX, 0)
            }
            MotionEvent.ACTION_UP -> {//ACTION_UP处理快速滑动到其他页面
                //相对于当前View滑动的距离,正为向左,负为向右
                val distance = scrollX - currentIndex * childWidth

                //必须滑动的距离要大于1/2个宽度,否则不会切换到其他页面
                if (Math.abs(distance) > childWidth / 2) {
                    if (distance > 0) {
                        currentIndex++
                    } else {
                        currentIndex--
                    }
                } else {
                    //调用该方法计算1000ms内滑动的平均速度
                    tracker!!.computeCurrentVelocity(1000)
                    val xV = tracker!!.xVelocity//获取到水平方向上的速度
                    //如果速度的绝对值大于50的话，就认为是快速滑动，就执行切换页面
                    if (Math.abs(xV) > 50) {
                        if (xV > 0) {
                            //大于0切换上一个页面
                            currentIndex--
                        } else {
                            //小于0切换到下一个页面
                            currentIndex++
                        }
                    }
                }
                currentIndex = if (currentIndex < 0) 0 else Math.min(currentIndex, childCount - 1)
                smoothScrollTo(currentIndex * childWidth, 0)
                //重置速度计算器
                tracker!!.clear()
            }
            else -> {}
        }
        lastX = x
        lastY = y
        return true
    }

    override fun computeScroll() {
        super.computeScroll()
        if (scroller!!.computeScrollOffset()) {
            scrollTo(scroller!!.currX, scroller!!.currY)
            postInvalidate()
        }
    }

    /**
     * 弹性滑动到指定位置
     */
    private fun smoothScrollTo(destX: Int, destY: Int) {
        scroller!!.startScroll(
            scrollX, scrollY, destX - scrollX,
            destY - scrollY, 1000
        )
        invalidate()
    }
}